﻿using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Model.DataManagement;
using Model.Entity;
using Model.Entity.Members;

namespace LibraryManagerTests.Model.DataManagement
{

    /// <summary>
    /// This class contains unit tests for Model.DataManagement.BorrowingManager.
    /// </summary>
    [TestClass]
    public class BorrowingManagerTests
    {

        class TestableBorrowingManager : BorrowingManager
        {
            public static Int32 FirstId { get { return FIRST_ID; } }

            public IList<Borrowing> Entities { get { return _entities; } }

            public IList<Borrowing> Edited { get { return _edited; } }

            public IList<Borrowing> New { get { return _new; } }

            public IList<Borrowing> Deleted { get { return _deleted; } }

            public IList<Borrowing> Managed { get { return _managed; } }

        }

        /// <summary>
        /// Tests for BorrowingManager.CanSave().
        /// </summary>
        [TestMethod]
        public void TestCanSave()
        {

            // ease of typing
            string borrowErr = BorrowingManager.BORROWDATE_MISSING_ERROR_HU;
            string returnErr = BorrowingManager.RETURNDATE_ERROR_HU;

            BorrowingManager bm = new BorrowingManager();
            Borrowing b = new Borrowing(1);
            Messages msg;

            // no borrowing date
            b.BorrowedDate = null;
            msg = bm.CanSave(b);

            Assert.AreEqual(1, msg.MessageList.Count);
            Assert.IsTrue(msg.MessageList.Contains(borrowErr));

            // has borrowing date
            b.BorrowedDate = DateTime.Now;
            msg = bm.CanSave(b);

            Assert.AreEqual(0, msg.MessageList.Count);

            // return date > borrowing date
            Borrowing b2 = new Borrowing(b.GetId(), null, null, b.BorrowedDate, b.BorrowedDate + new TimeSpan(1000));
            b.Assign(b2);
            msg = bm.CanSave(b);

            Assert.AreEqual(0, msg.MessageList.Count);

            // return date < borrowing date
            b2 = new Borrowing(b.GetId(), null, null, b.BorrowedDate, b.BorrowedDate - new TimeSpan(1000));
            b.Assign(b2);
            msg = bm.CanSave(b);

            Assert.AreEqual(1, msg.MessageList.Count);
            Assert.IsTrue(msg.MessageList.Contains(returnErr));
        }

        /// <summary>
        /// Tests BorrowingManager.Marshal(), BorrowingManager.Unmarshal().
        /// </summary>
        [TestMethod]
        public void TestMarshalUnmarshal()
        {
            int tests = 1000;

            BorrowingManager lm = new BorrowingManager();
            Borrowing l;
            Borrowing u; // unmarshalled
            Random r = new Random();

            /*
             * Tests by fields
             */

            // all fields
            Int32 id;
            Int32 memberId;
            Int32 bookId;
            DateTime? borrowingDate;
            DateTime? returnDate;

            byte[] m; // marshalled
            for (int i = 0; i < tests; i++)
            {
                id = r.Next(TestableBookManager.FirstId, Int32.MaxValue);
                memberId = r.Next(TestableBookManager.FirstId, Int32.MaxValue);
                bookId = r.Next(TestableBookManager.FirstId, Int32.MaxValue);
                if (r.Next() % 3 == 0) { borrowingDate = null; }
                else { borrowingDate = new DateTime(r.Next(0, int.MaxValue)); }
                if (r.Next() % 3 == 0) { returnDate = null; }
                else { returnDate = new DateTime(r.Next(0, int.MaxValue)); }

                l = new Borrowing(id, new Other(memberId), new Book(bookId), borrowingDate, returnDate);
                m = lm.Marshal(l);
                u = lm.Unmarshal(m);

                Assert.AreEqual(id, u.GetId());
                Assert.AreEqual(memberId, u.Member.GetId());
                Assert.AreEqual(bookId, u.Book.GetId());
                if (null == borrowingDate) { Assert.IsNull(u.BorrowedDate); }
                else
                {
                    Assert.AreEqual(borrowingDate.Value.Year, u.BorrowedDate.Value.Year);
                    Assert.AreEqual(borrowingDate.Value.Month, u.BorrowedDate.Value.Month);
                    Assert.AreEqual(borrowingDate.Value.Day, u.BorrowedDate.Value.Day);
                }
                if (null == returnDate) { Assert.IsNull(u.ReturnedDate); }
                else
                {
                    Assert.AreEqual(returnDate.Value.Year, u.ReturnedDate.Value.Year);
                    Assert.AreEqual(returnDate.Value.Month, u.ReturnedDate.Value.Month);
                    Assert.AreEqual(returnDate.Value.Day, u.ReturnedDate.Value.Day);
                }
            }

        }

        /// <summary>
        /// Tests BorrowingManager.LoadEntities(), PersistEntities().
        /// </summary>
        [TestMethod]
        public void TestLoadPersist()
        {
            // entities to persist
            int entities = 100;
            Random r = new Random();
            TestableBorrowingManager tlmP = new TestableBorrowingManager();

            Int32 id;
            Int32 memberId;
            Int32 bookId;
            DateTime? borrowingDate;
            DateTime? returnDate;

            // generate random entities
            for (int i = 0; i < entities; i++)
            {
                id = r.Next(TestableBookManager.FirstId, Int32.MaxValue);
                memberId = r.Next(TestableBookManager.FirstId, Int32.MaxValue);
                bookId = r.Next(TestableBookManager.FirstId, Int32.MaxValue);
                if (r.Next() % 3 == 0) { borrowingDate = null; }
                else { borrowingDate = new DateTime(r.Next(0, int.MaxValue)); }
                if (r.Next() % 3 == 0) { returnDate = null; }
                else { returnDate = new DateTime(r.Next(0, int.MaxValue)); }

                Borrowing b = new Borrowing(id, new Other(memberId), new Book(bookId), borrowingDate, returnDate);

                // place the entity into one of the managed lists, based on i's value
                tlmP.Managed.Add(b);
                switch (i % 4)
                {
                    case 0:
                        tlmP.Entities.Add(b);
                        break;

                    case 1:
                        tlmP.Edited.Add(b);
                        break;

                    case 2:
                        tlmP.New.Add(b);
                        break;

                    case 3:
                        tlmP.Deleted.Add(b);
                        break;
                }
            }

            // persist the entities & wait for the os
            tlmP.PersistEntities();
            Thread.Sleep(1000);

            // load them back
            TestableBorrowingManager tlmL = new TestableBorrowingManager();
            tlmL.LoadEntities();

            // test the results
            //

            // list counts
            Assert.AreEqual(tlmP.Managed.Count - tlmP.Deleted.Count, tlmL.Managed.Count);
            Assert.AreEqual(tlmP.Entities.Count + tlmP.Edited.Count + tlmP.New.Count, tlmL.Entities.Count);
            Assert.AreEqual(0, tlmL.Edited.Count);
            Assert.AreEqual(0, tlmL.New.Count);
            Assert.AreEqual(0, tlmL.Deleted.Count);

            // list values
            foreach (Borrowing l in tlmP.Entities)
            {
                bool found = false;
                foreach (Borrowing o in tlmL.Entities)
                {
                    var ml = tlmP.Marshal(l);
                    var mo = tlmP.Marshal(o);
                    if (ml.SequenceEqual(mo)) { found = true; }
                }

                Assert.IsTrue(found);
            }
            foreach (Borrowing l in tlmP.Edited)
            {
                bool found = false;
                foreach (Borrowing o in tlmL.Entities)
                {
                    var ml = tlmP.Marshal(l);
                    var mo = tlmP.Marshal(o);
                    if (ml.SequenceEqual(mo)) { found = true; }
                }

                Assert.IsTrue(found);
            }
            foreach (Borrowing l in tlmP.New)
            {
                bool found = false;
                foreach (Borrowing o in tlmL.Entities)
                {
                    var ml = tlmP.Marshal(l);
                    var mo = tlmP.Marshal(o);
                    if (ml.SequenceEqual(mo)) { found = true; }
                }

                Assert.IsTrue(found);
            }

            var list = tlmP.Managed.Where(item => !(tlmP.Deleted.Contains(item))).ToList();
            foreach (Borrowing l in tlmL.Managed)
            {
                bool found = false;
                foreach (Borrowing o in list)
                {
                    var ml = tlmP.Marshal(l);
                    var mo = tlmP.Marshal(o);
                    if (ml.SequenceEqual(mo)) { found = true; }
                }

                Assert.IsTrue(found);
            }

            // delete the file
            File.Delete(tlmP.GetDatafileName());
        }

    }

}
